﻿using Microsoft.Extensions.DependencyInjection;

using Utils;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Threading.Tasks;
using System.Collections;

namespace Utils.JobTask
{
    class JobInstance
    {
        private readonly SortedQueue<JobQueueGroup> queue;


        private readonly Type type;


        private readonly MethodInfo methodInfo;

        public JobInstance(Type type, MethodInfo methodInfo)
        {
            this.queue = new SortedQueue<JobQueueGroup>(ComparerHelper.CreateComparer<JobQueueGroup>(a => a.ExecuteTime));
            this.type = type;
            this.methodInfo = methodInfo;
        }



        /// <summary>
        /// 追加任务
        /// </summary>
        /// <param name="executeTime"></param>
        /// <param name="job"></param>
        public void Enqueue(JobQueue job)
        {
            lock (this)
            {
                var group = queue.Get(a => a.ExecuteTime == job.ExecuteTime);
                if (group == null)
                {
                    queue.Enqueue(new JobQueueGroup(job));
                }
                else
                {
                    group.Jobs.Enqueue(job);
                }
            }
        }

        public void Execute(IServiceProvider serviceProvider, Action<Exception> callException)
        {
            if (queue.Count == 0)
            {
                return;
            }
            if (!queue.TryPeek(out var jobgroup) || jobgroup.ExecuteTime > DateTime.Now.ToUnixTimestamp())
            {
                return;
            }
            queue.Dequeue();
            Task.Run(() =>
            {
                var isSuccess = true;

                while (jobgroup.Jobs.Count > 0)
                {
                    var job = jobgroup.Jobs.Dequeue();
                    if (!Invoke(serviceProvider, job, callException))
                    {
                        isSuccess = false;
                        jobgroup.Jobs.Enqueue(job);
                    }
                }
                if (!isSuccess)
                {
                    queue.Enqueue(jobgroup);
                }
            });
        }

        public bool Invoke(IServiceProvider serviceProvider, JobQueue job, Action<Exception> callException)
        {
            try
            {
                using (var scope = serviceProvider.CreateScope())
                {
                    var obj = scope.ServiceProvider.GetRequiredService(type);
                    var result = methodInfo.Invoke(obj, job.Args);
                    if (result is Task task)
                    {
                        task.GetAwaiter().GetResult();
                    }
                }
                return true;
            }
            catch (CustomException e)
            {
                if(e.Code == (int)ErrorType.GeneralError)
                {
                    return true;
                }
                callException?.Invoke(e.InnerException ?? e);
                return job.Reduce();
            }
            catch (Exception e)
            {
                callException?.Invoke(e.InnerException ?? e);
                return job.Reduce();
            }
        }


    }
}
